package treeedit;

import java.util.ArrayList;
import java.util.List;

import javax.swing.tree.TreePath;

public class CodeNode 
{
	private List<CodeNode> children = new ArrayList<CodeNode>() ;
	private CodeNode parent;
	private String code;
	private static Lisper lisper = Lisper.getInstance();
	boolean marked = false;
	boolean string = false;
	boolean quote = false;
	
	public CodeNode( String code, CodeNode parent)
	{
		this.code = code;
		this.parent = parent;
	}
	
	public CodeNode( String code)
	{
		this(code,null);
	}
	
	public void add( CodeNode child)
	{
		child.parent = this;
		if(!children.contains(child))children.add(child);
	}
	
	public void setCode(String code)
	{
		this.code = code;
	}
	
	public String getCode()
	{
		return code;
	}

	public CodeNode getParent() {
		return parent;
	}

	public void setParent(CodeNode parent) {
		this.parent = parent;
	}

	public List<CodeNode> getChildren() {
		return children;
	}

	public void remove( CodeNode child)
	{
		children.remove(child);
	}
	
	@Override 
	public String toString ()
	{
		return code;
	}
	
	public void insertAtIndex(CodeNode child,int index)
	{
		child.parent = this;
		if(!children.contains(child))
		{
			children.add(index, child);
		}
	}
	
	public void insertAfter(CodeNode node, CodeNode child)
	{
		int index;
		if((index = children.indexOf(node)) == -1 ) return;
		insertAtIndex(child, index + 1);
	}
	
	public void insertBefore(CodeNode node, CodeNode child)
	{
		int index;
		if((index = children.indexOf(node)) == -1 ) 
		{
			System.out.println("Debug");
			return;
		}
		insertAtIndex(child, index);
	}

	public boolean regress (CodeNode node)
	{
		boolean reg = false;
		
		if(parent == null) return reg;
		int index = children.indexOf(node);
		if(index == -1) return reg;
		
		System.out.println("regress");
		
		
		for(int i = children.size()-1 ; i >index;i--)
		{
			System.out.println(i);
			parent.insertAfter(this,children.get(i));
			reg =true;
		}
		
		int size = children.size();
		for(int i = index + 1;i<size;i++)
		{
			System.out.println(i);
			children.remove(index + 1);
		}
		
		return reg;
	}
	
	public boolean isFunction ()
	{
		if(getChildren().size() == 0) return false;
		if(!code.equals("( )")) return false;
		
		return 	lisper.isFunction(children.get(0).getCode());
		
	}
	
	public synchronized String getRepresentation()
	{
		String ret;
 		boolean rep = false;
		if(isFunction())
		{
			ret = children.get(0).getCode();
			rep = true;
		}
 		else
 			ret = code;
		//System.out.println("getRepresentation(): " + ret + "  IS FUNCTION: " + rep + " LENGTH " + ret.length());
		
		return ret;	}
	
	public void setRepresentation( String code)
	{
		System.err.println("CODE =" + code);
		if( isFunction())
		{
//			System.err.println("IS FUNCTION");
			children.get(0).setCode(code);
		}
		else
		{
			setCode(code);
		}
	}

	public String getFileContent() {
		String ret = getFormattedRepresentation();
//		System.out.println("getFileContent()()() " + ret);
		if(ret.equals("Root")) return "";
		ret = ret.substring(ret.indexOf("(") + 1,ret.lastIndexOf(")"));
		return ret;
	}

	public String  getFormattedRepresentation() {
		int length = getFullRepresentation().length();
		if (length < 70 || children.size()==0)
			return getFullRepresentation();
		else {
			StringBuffer ret = new StringBuffer(getBegin());
			int i = 0;
			for (CodeNode child : children) {
				if (i++>0) ret.append(" \n"); else ret.append(" ");
				ret.append(child.getFormattedRepresentation());
			}
			ret.append(")");
			return ret.toString();
		}
	}

	private String getBegin() {
		if(code.equals("`( )")) return "`(";
		if(code.equals("'( )")) return "'(";
		if(code.equals("#'( )")) return "#'(";
		if(code.equals("#`( )")) return "#`(";
		return "(";
	}

	public String getShortcut() {
		// So the node won't traverse hundred times
		if(couldBeShortened()) {
			StringBuffer ret = new StringBuffer(getBegin());
			for(CodeNode child : children) {
				ret.append(" ");
				ret.append(child.getShortcut());
			}
			ret.append(" )");
			return ret.toString();
		} 
		if ( getRepresentation().toLowerCase().startsWith("defun") && (children.size() > 1) ) {
			return getRepresentation() + " "  + children.get(1).getRepresentation();
		}
		return getRepresentation();
	}
	
	public String  getFullRepresentation()
	{
		if(!(getChildren().size() == 0))
		{
			StringBuffer ret = new StringBuffer(getBegin());
			for(CodeNode child : children) {
				ret.append(" ");
				ret.append(child.getFullRepresentation());
			}
			ret.append(" )");
			return ret.toString();
		}
		else
		{
			if(isString())
			{
				return "\"" + getRepresentation() + "\"";
			}
			else
			{
				return getRepresentation();
			}
		}
		
	}

	private boolean couldBeShortened() {
		if(getLength(3) > 40) return false;
		if (children.size()==0) return false;
		return true;
	}

	private int getLength(int depth) {
		if(depth > 0) {
			int ret = 2 + code.length();
			for(CodeNode child : children)
				ret+=child.getLength(depth-1);
			return ret;
		} 
		return code.length();
	}

	public boolean isMarked() {
		return marked;
	}

	public void setMarked(boolean marked) {
		this.marked = marked;
	}
	
	public boolean isAncestorOf ( CodeNode child)
	{
		for(CodeNode c : children)
		{
			if(c.equals(child)) return true;
			if(c.children.size() != 0)
			{
				if(c.isAncestorOf(child)) return true;
			}
		}
		return false;
	}
	
	public CodeNode getNext( CodeNode child)
	{
		int size = children.size();
		int index = children.indexOf(child);
		System.out.println("Next " + index);
		
		if(index == -1) return null;
		
		if(index + 1 < size) return children.get(index+1);
		else return null;
		
	}
	
	public CodeNode getPrevious( CodeNode child)
	{
		int index = children.indexOf(child);
//		System.out.println("Prev " + index);
		
		if(index == -1) return null;
		//Function nodes
		if(index == 1 && isFunction()) return null;
		
		if(index -1 >=  0 ) return children.get(index-1);
		else return null;
		
	}

	public boolean isString() {
		return string;
	}

	public void setString(boolean string) {
		this.string = string;
	}

	public boolean isQuote() {
		return quote;
	}

	public void setQuote(boolean quote) {
		this.quote = quote;
	}

	public void exchange(CodeNode newChild)
	{
		CodeNode parent = getParent();
		if(parent == null) return ;
		
		int index = parent.children.indexOf(this);
		if(index == -1 || newChild == null) return ;
		
//		System.out.println("Switching ");
		newChild.parent = parent;
		//this.parent = null; //May conflict with some stuff when not propertly handled
		
		parent.children.set(index, newChild);
	}
	
	public boolean swap(CodeNode node)
	{
		CodeNode thisParent = parent;
		CodeNode nodeParent = node.getParent();
		
		//Not switching father-son nodes
		if(children.indexOf(node) != -1 || node.children.indexOf(this) != -1) return false ;
		
		int thisIndex = thisParent.children.indexOf(this);
		int nodeIndex = nodeParent.children.indexOf(node);
		
		thisParent.children.set(thisIndex, node);
		node.parent = thisParent;
		
		nodeParent.children.set(nodeIndex, this);
		this.parent = nodeParent;
		
		return true;
	}
	
	public CodeNode clone() 
	{
		CodeNode newNode = new CodeNode("");
		newNode.parent = parent;
		newNode.code = code;
		newNode.lisper=  lisper;
		newNode.marked = false;
		newNode.string = string;
		newNode.quote = quote;
		for(CodeNode c : children)
		{
			newNode.add(c.clone());
		}
		return newNode;
	}
	
	public boolean isParentOf(CodeNode c)
	{
		return children.contains(c);
	}
	
	public boolean isValidPath(TreePath path)
	{
		if(path == null) return false;
		
		CodeNode last = (CodeNode)path.getLastPathComponent();
		
		CodeNode parent = last.getParent();
		
		if(last != this) return false;
		//Root
		if(parent == null && path.getPathCount() == 1) return true;
		if(parent == null ) return false;
		
		if(!parent.isParentOf(this)) return false;
		
		//If everything here ok
		
		return parent.isValidPath(path.getParentPath());
		
		
		
	}
	
	
}
